package cn.nkpro.elcube.co;

import cn.nkpro.elcube.annotation.NkNote;
import cn.nkpro.elcube.annotation.NkScriptType;
import cn.nkpro.elcube.exception.NkDefineException;
import cn.nkpro.elcube.utils.ClassUtils;
import cn.nkpro.elcube.utils.GroovyUtils;
import cn.nkpro.elcube.utils.ResourceUtils;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

public interface NkCustomScriptHelper {

    ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

    static NkScriptV loadScriptFromClassPath(String[] componentBasePackages, Class type, String beanName) {

        String className = type.getSimpleName();

        List<String> groovyCode = findResource(componentBasePackages,className + ".groovy");
        if (!groovyCode.isEmpty()) {
            List<String> vueMainCode = findResource(componentBasePackages,className + ".vue");
            List<String> vueDefsCode = findResource(componentBasePackages,className + "Def*.vue");
            NkScriptV scriptV = new NkScriptV();
            scriptV.setScriptName(beanName);
            scriptV.setVersion("@");
            scriptV.setGroovyMain(groovyCode.stream().findFirst().orElse(null));
            scriptV.setVueMain(vueMainCode.stream().findFirst().orElse(null));
            scriptV.setVueDefs(CollectionUtils.isEmpty(vueDefsCode)?null: JSON.toJSONString(vueDefsCode));
            scriptV.setState("Active");

            Class<?> groovy = GroovyUtils.compileGroovy(className, scriptV.getGroovyMain());
            List<Class<?>> interfaces = org.apache.commons.lang3.ClassUtils.getAllInterfaces(groovy);

            if(interfaces.contains(NkCustomScriptObject.class)){
                scriptV.setScriptType(
                        interfaces.stream()
                                .map(i -> i.getAnnotation(NkScriptType.class))
                                .filter(Objects::nonNull)
                                .findFirst()
                                .map(NkScriptType::value)
                                .orElse("Service")
                );
            }else{
                throw new NkDefineException("组件必须实现NkScriptObject接口");
            }

            NkNote annotation = groovy.getAnnotation(NkNote.class);
            scriptV.setScriptDesc(annotation != null ? annotation.value() : beanName);
            return scriptV;
        }

        NkScriptV scriptV = new NkScriptV();
        scriptV.setScriptType("Unknown");
        scriptV.setScriptName(beanName);
        scriptV.setVersion("@");
        scriptV.setGroovyMain(null);
        scriptV.setVueMain(null);
        scriptV.setVueDefs(null);
        scriptV.setState("Native");
        return scriptV;
    }



    static String parseComponentName(Class<?> clazz){

        Component component = clazz.getDeclaredAnnotation(Component.class);
        if(component!=null && StringUtils.isNotBlank(component.value()))
            return component.value();

        Service service = clazz.getDeclaredAnnotation(Service.class);
        if(service!=null && StringUtils.isNotBlank(service.value()))
            return service.value();

        return ClassUtils.decapitateClassName(clazz.getSimpleName());
    }

    static List<String> findResource(String[] componentBasePackages, String resourceName){
        try {
            List<Resource> resources = new ArrayList<>();
            for (String path : componentBasePackages) {
                resources.addAll(Arrays.asList(resourcePatternResolver.getResources(String.format("classpath*:/%s/**/%s", packageToPath(path), resourceName))));
            }
            return resources.stream()
                    .sorted(Comparator.comparing(resource -> StringUtils.defaultString(resource.getFilename())))
                    .map(ResourceUtils::readText)
                    .collect(Collectors.toList());
        }catch (Exception e){
            e.printStackTrace();
        }
        return Collections.emptyList();
    }

    static String packageToPath(String packageName){
        packageName = packageName.replaceAll("[.]","/");
        if(packageName.startsWith("/")){
            packageName = packageName.substring(1);
        }
        if(packageName.endsWith("/")){
            packageName = packageName.substring(0,packageName.length()-1);
        }
        return packageName;
    }
}
